
#include "mythread.h"



MyThread::MyThread(QString file, QString dir)
{
    this->file = file;
    this->dir = dir;
}

void MyThread::run()
{
    this->search(file,dir);

};

void findPairs(CvSeq *objectDescriptors, CvSeq *imageDescriptors, vector<int>& ptpairs)
{
    //Вся функция целиком позаимствована из документации. Простите меня, если сможете.
    //Впрочем, документация для того и нужна, правда?

    //Не знаю, что это ;_;
    int length = (int)(objectDescriptors->elem_size/sizeof(float));

    //Матрицы для хранения данных
    cv::Mat m_object(objectDescriptors->total, length, CV_32F);
    cv::Mat m_image(imageDescriptors->total, length, CV_32F);

    //Копирование дескрипторов 1
    CvSeqReader obj_reader;
    float* obj_ptr = m_object.ptr<float>(0);
    cvStartReadSeq( objectDescriptors, &obj_reader );
    for(int i = 0; i < objectDescriptors->total; i++ )
    {
        const float* descriptor = (const float*)obj_reader.ptr;
        CV_NEXT_SEQ_ELEM( obj_reader.seq->elem_size, obj_reader );
        memcpy(obj_ptr, descriptor, length*sizeof(float));
        obj_ptr += length;
    }

    //Копирование дескрипторов 2
    CvSeqReader img_reader;
    float* img_ptr = m_image.ptr<float>(0);
    cvStartReadSeq( imageDescriptors, &img_reader );
    for(int i = 0; i < imageDescriptors->total; i++ )
    {
        const float* descriptor = (const float*)img_reader.ptr;
        CV_NEXT_SEQ_ELEM( img_reader.seq->elem_size, img_reader );
        memcpy(img_ptr, descriptor, length*sizeof(float));
        img_ptr += length;
    }

    //Собственно, сравнение дескрипторов
    cv::Mat m_indices(objectDescriptors->total, 2, CV_32S);
    cv::Mat m_dists(objectDescriptors->total, 2, CV_32F);
    cv::flann::Index flann_index(m_image, cv::flann::KDTreeIndexParams(4));  // using 4 randomized kdtrees
    flann_index.knnSearch(m_object, m_indices, m_dists, 2, cv::flann::SearchParams(64) ); // maximum number of leafs checked

    int* indices_ptr = m_indices.ptr<int>(0);
    float* dists_ptr = m_dists.ptr<float>(0);
    for (int i=0;i<m_indices.rows;++i) {
        if (dists_ptr[2*i]<0.6*dists_ptr[2*i+1]) {
                ptpairs.push_back(i);
                ptpairs.push_back(indices_ptr[2*i]);
        }
    }
}

char* strToChar(QString str)
{
    //QByteArray enc = str.toUtf8();
    QByteArray enc = str.toLocal8Bit();
    char* filename = new char[enc.size()+1];
    strcpy(filename,enc.data());
    return filename;

}




int MyThread::search(QString filename_string, QString directory_string)
{

    CvSeq *objectKeypoints = 0, *objectDescriptors = 0;
    CvSeq *sceneKeypoints = 0, *sceneDescriptors = 0;
    CvMemStorage* storage = cvCreateMemStorage(0);
    char* filename = strToChar(filename_string);
    qDebug()<<"HUITA "<<filename_string;

    //Загружаем изображение
    IplImage* object = cvLoadImage(filename, CV_LOAD_IMAGE_GRAYSCALE);
    //Проверяем, загрузилось ли изображение для поиска
    if( !object )
    {
        qDebug()<<"ERROR: failed to load image "<< filename_string;
        return 1;
    }

    //Изображение преобразуется в черно-белое. Кажется.
    IplImage* object_color = cvCreateImage(cvGetSize(object), 8, 3);
    cvCvtColor( object, object_color, CV_GRAY2BGR );

    //Если настройка включена - пережать изображение
    if (GlobalSettings.resizeEnabled)
        object = resizeImage(object, GlobalSettings.resizeTo);


    //Настройки SURF
    int betterDescriptors;
    if (GlobalSettings.betterDescriptorsEnabled)
        betterDescriptors = 1;
    else
        betterDescriptors = 0;

    CvSURFParams parameters = cvSURFParams(GlobalSettings.threshold, betterDescriptors);

    //Собственно извлечение дескрипторов
    cvExtractSURF( object, 0, &objectKeypoints, &objectDescriptors, storage, parameters );

    int i = 0;

    //Извлекаем список файлов из каталога
    QDir dir(directory_string);
    QStringList filters;
    filters << "*.jpg" << "*.png" << "*.gif";
    QStringList list = dir.entryList(filters, QDir::Files);
    //Проверяем, что в каталоге есть изображения
    if (list.size() == 0)
    {
        qDebug()<<"ERROR: there are no images in the folder";
        return 1;
    }
    emit total(list.size());

    //Сравниваем каждое изображение в каталоге с искомым
    for (i = 0; i<list.size(); i++){
        //Меняем имя файла в диалоге
        emit change(list.at(i));

        CvMemStorage* storage1 = cvCreateMemStorage(0);




        //Грузим изображение для сравнения
        IplImage* scene = cvLoadImage(strToChar(dir.absolutePath()+QDir::separator()+list.at(i)), CV_LOAD_IMAGE_GRAYSCALE);
        //Проверяем, загрузилось ли
        if( !scene )
        {
            qDebug()<<"ERROR: failed to load image "<< filename_string;
            continue;
        }

        //Изображение преобразуется в черно-белое. Кажется.
        IplImage* scene_color = cvCreateImage(cvGetSize(scene), 8, 3);
        cvCvtColor( scene, scene_color, CV_GRAY2BGR );


        //Если настройка включена - пережать изображение
        if (GlobalSettings.resizeEnabled)
            scene = resizeImage(scene, GlobalSettings.resizeTo);


        //Извлекаем из него дескрипторы
        try
        {
            cvExtractSURF( scene, 0, &sceneKeypoints, &sceneDescriptors, storage1, parameters );
        }
        catch(...)
        {
            qDebug()<<"ERROR: cvExtractSURF failed on image: "<<list.at(i);
            continue;
        }


        //Ищем пары.
        //Программа здесь частенько падает, так что поставил обработчик исключений.
        //Если обосремся - молча переходим на следующую итерацию цикла.
        vector<int> ptpairs;
        try
        {
        findPairs(objectDescriptors, sceneDescriptors, ptpairs);
        }
        catch(...)
        {
            qDebug()<<"ERROR: findPairs failed on image: "<<list.at(i);
            //Освобождаем память от изображения из каталога
            cvReleaseImage(&scene);
            cvReleaseMemStorage(&storage1);
            continue;
        }

        //Подсчитываем процент соответствий...
        int percentage = int((float)ptpairs.size()/2.0/(float)objectDescriptors->total*100.0);
        //...и выводим результат...
        qDebug() << "RESULT: Image " << list.at(i) << " corresponds at "<< percentage <<"%";
        //...а еще записываем его в листы, если процент больше минимального:

        if (percentage >= GlobalSettings.minimalCorrespondence)
        {
            resName.append(list.at(i));
            resPerc.append(percentage);
        }



        //Освобождаем память от изображения из каталога
        cvReleaseImage(&scene);
        cvReleaseImage(&scene_color);
        //cvReleaseImage(&dummy);

        //cvReleaseMemStorage(&sceneDescriptors->storage);
        //cvReleaseMemStorage(&sceneKeypoints->storage);
        //sceneKeypoints = 0;
        //sceneDescriptors = 0;
        cvReleaseMemStorage(&storage1);

        //Меняем проценты в прогрессбаре диалога
        emit bump(i);


    }

    //Освобождаем изображения - уменьшаем утечки памяти
    cvReleaseImage(&object);
    cvReleaseImage(&object_color);
    cvReleaseMemStorage(&storage);
    return 0;


}

IplImage* resizeImage(IplImage* image, int squareSize){
    //int squareSize = 600;
    int nx, ny;

    if (image->width >= image->height && image->width > squareSize)
    {
        nx = squareSize;
        ny =int( (float) squareSize * image->height / image->width);
    }
    else if (image->width < image->height && image->height > squareSize)
    {
        ny = squareSize;
        nx = int((float)squareSize * image->width / image->height);
    }
    else
    {
        qDebug()<<"Image is small enough, so no resizing...";
        return image;
    };
    IplImage* dummy = NULL;
    dummy = cvCreateImage(cvSize(nx,ny), image->depth, image->nChannels);
    //qDebug()<<dummy->width<<" "<<dummy->height;
    cvResize(image, dummy);
    //Следующая строка решает последнюю проблему с памятью, ура!
    cvReleaseImage(&image);
    image = dummy;
    //cvCopy(dummy, image, NULL);

    return dummy;
}
